package study.kit.global.id.controller;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Slf4j
@RestController
public class RedisIncrController {

    // 步长
    public final static int SIZE = 10;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 缓存
    private BlockingQueue<Long> cache = new ArrayBlockingQueue<>(SIZE);
    private ConcurrentHashMap<String, ArrayBlockingQueue<Long>> multiCache = new ConcurrentHashMap<>();

    // 锁
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    private ReadWriteLock multiLock = new ReentrantReadWriteLock();

    @RequestMapping("/redis/getGlobalIdV1")
    public Long getGlobalIdV1() {
        // 写
        Lock write = lock.writeLock();
        try {
            write.lock();
            if (cache.isEmpty()) {
                // 预取
                Long maxId = redisTemplate.opsForValue().increment("global_id", SIZE);
                Long startId = maxId - SIZE;
                log.info(">>>>>> 批量预取: [{}, {})", startId, maxId);

                // 缓存
                for (long i = startId; i < maxId; i++) {
                    cache.add(i);
                }
            }
        } finally {
            write.unlock();
        }

        // 读
        Lock read = lock.readLock();
        Long globalId = null;
        try {
            read.lock();

            globalId = cache.poll();
        } catch (Exception ex) {
            log.info("{}", ex);
        } finally {
            read.unlock();
        }

        //
        return globalId;
    }

    @RequestMapping("/redis/getGlobalIdV2")
    public Long getGlobalIdV2(String businessType) {
        //
        ArrayBlockingQueue<Long> myQueue = multiCache.get(businessType);
        if (Objects.isNull(myQueue)) {
            myQueue = new ArrayBlockingQueue<>(SIZE);
            multiCache.put(businessType, myQueue);
        }
        if (myQueue.isEmpty()) {
            //
            // 预取
            Long maxId = redisTemplate.opsForValue().increment(businessType, SIZE);
            Long startId = maxId - SIZE;
            log.info(">>>>>> {}: 批量预取, [{}, {})", businessType, startId, maxId);

            // 缓存
            for (long i = startId; i < maxId; i++) {
                myQueue.add(i);
            }
        }

        // 取
        Long globalId = null;
        try {
            globalId = myQueue.poll();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return globalId;
    }
}
